package com.gzc.just.play.last.war.centralserver.guild;



import com.gzc.just.play.last.war.centralserver.guild.model.Guild;

import com.gzc.just.play.last.war.centralserver.guild.model.GuildMember;

import com.gzc.just.play.last.war.centralserver.guild.repository.GuildRepository;

import com.gzc.just.play.last.war.centralserver.guild.repository.GuildMemberRepository;

import com.gzc.just.play.last.war.centralserver.repository.CentralPlayerRepository;

import com.gzc.just.play.last.war.centralserver.model.Player;

import lombok.RequiredArgsConstructor;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;



import java.util.*;

import java.util.concurrent.CompletableFuture;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;



@Slf4j

@Service("central-guildManager")
@RequiredArgsConstructor

public class GuildManager {



    private final GuildRepository guildRepository;

    private final GuildMemberRepository guildMemberRepository;

    private final CentralPlayerRepository playerRepository;

    

    private final Map<Long, Guild> guildCache = new ConcurrentHashMap<>();

    

    private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();

    

    @Transactional

    public CompletableFuture<Guild> createGuild(Long playerId, String guildName, String guildDesc) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<Player> playerOptional = playerRepository.findById(playerId);

                if (playerOptional.isEmpty()) {

                    log.warn("Player not found: {}", playerId);

                    return null;

                }

                

                Player player = playerOptional.get();

                

                Optional<GuildMember> existingMember = guildMemberRepository.findByPlayerId(playerId);

                if (existingMember.isPresent()) {

                    log.warn("Player {} is already in a guild", playerId);

                    return null;

                }

                

                Optional<Guild> existingGuild = guildRepository.findByName(guildName);

                if (existingGuild.isPresent()) {

                    log.warn("Guild name already exists: {}", guildName);

                    return null;

                }

                

                if (player.getGold() < 10000 || player.getDiamond() < 100) {

                    log.warn("Player {} does not have enough resources to create guild", playerId);

                    return null;

                }

                

                player.setGold(player.getGold() - 10000);

                player.setDiamond(player.getDiamond() - 100);

                playerRepository.save(player);

                

                Guild guild = new Guild();

                guild.setName(guildName);

                guild.setDescription(guildDesc);

                guild.setLeaderId(playerId);

                guild.setLevel(1);

                guild.setExp(0);

                guild.setMemberCount(1);

                guild.setMaxMembers(50);

                guild.setCreateTime(System.currentTimeMillis());

                

                guild = guildRepository.save(guild);

                

                GuildMember leader = new GuildMember();

                leader.setGuildId(guild.getId());

                leader.setPlayerId(playerId);

                leader.setPlayerName(player.getName());

                leader.setLevel(player.getLevel());

                leader.setRole(GuildMember.Role.LEADER);

                leader.setContribution(0);

                leader.setJoinTime(System.currentTimeMillis());

                

                guildMemberRepository.save(leader);

                

                guildCache.put(guild.getId(), guild);

                

                log.info("Player {} created guild: {}", playerId, guildName);

                return guild;

                

            } catch (Exception e) {

                log.error("Error creating guild {} by player {}", guildName, playerId, e);

                return null;

            }

        }, executorService);

    }

    

    @Transactional

    public CompletableFuture<Boolean> joinGuild(Long playerId, Long guildId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<GuildMember> existingMember = guildMemberRepository.findByPlayerId(playerId);

                if (existingMember.isPresent()) {

                    log.warn("Player {} is already in a guild", playerId);

                    return false;

                }

                

                Guild guild = getGuild(guildId);

                if (guild == null) {

                    log.warn("Guild not found: {}", guildId);

                    return false;

                }

                

                if (guild.getMemberCount() >= guild.getMaxMembers()) {

                    log.warn("Guild {} is full", guildId);

                    return false;

                }

                

                Optional<Player> playerOptional = playerRepository.findById(playerId);

                if (playerOptional.isEmpty()) {

                    log.warn("Player not found: {}", playerId);

                    return false;

                }

                

                Player player = playerOptional.get();

                

                GuildMember member = new GuildMember();

                member.setGuildId(guildId);

                member.setPlayerId(playerId);

                member.setPlayerName(player.getName());

                member.setLevel(player.getLevel());

                member.setRole(GuildMember.Role.MEMBER);

                member.setContribution(0);

                member.setJoinTime(System.currentTimeMillis());

                

                guildMemberRepository.save(member);

                

                guild.setMemberCount(guild.getMemberCount() + 1);

                guildRepository.save(guild);

                

                guildCache.put(guildId, guild);

                

                log.info("Player {} joined guild: {}", playerId, guildId);

                return true;

                

            } catch (Exception e) {

                log.error("Error joining player {} to guild {}", playerId, guildId, e);

                return false;

            }

        }, executorService);

    }

    

    @Transactional

    public CompletableFuture<Boolean> leaveGuild(Long playerId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<GuildMember> memberOptional = guildMemberRepository.findByPlayerId(playerId);

                if (memberOptional.isEmpty()) {

                    log.warn("Player {} is not in a guild", playerId);

                    return false;

                }

                

                GuildMember member = memberOptional.get();

                Long guildId = member.getGuildId();

                

                if (member.getRole() == GuildMember.Role.LEADER) {

                    log.warn("Guild leader {} cannot leave guild directly", playerId);

                    return false;

                }

                

                guildMemberRepository.delete(member);

                

                Guild guild = getGuild(guildId);

                if (guild != null) {

                    guild.setMemberCount(guild.getMemberCount() - 1);

                    guildRepository.save(guild);

                    guildCache.put(guildId, guild);

                }

                

                log.info("Player {} left guild: {}", playerId, guildId);

                return true;

                

            } catch (Exception e) {

                log.error("Error leaving guild for player {}", playerId, e);

                return false;

            }

        }, executorService);

    }

    

    public CompletableFuture<Guild> getPlayerGuild(Long playerId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<GuildMember> memberOptional = guildMemberRepository.findByPlayerId(playerId);

                if (memberOptional.isEmpty()) {

                    return null;

                }

                

                Long guildId = memberOptional.get().getGuildId();

                return getGuild(guildId);

                

            } catch (Exception e) {

                log.error("Error getting guild for player {}", playerId, e);

                return null;

            }

        }, executorService);

    }

    

    private Guild getGuild(Long guildId) {

        Guild guild = guildCache.get(guildId);

        

        if (guild == null) {

            Optional<Guild> guildOptional = guildRepository.findById(guildId);

            if (guildOptional.isPresent()) {

                guild = guildOptional.get();

                guildCache.put(guildId, guild);

            }

        }

        

        return guild;

    }

    

    @Transactional

    public CompletableFuture<Boolean> upgradeGuild(Long playerId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<GuildMember> memberOptional = guildMemberRepository.findByPlayerId(playerId);

                if (memberOptional.isEmpty()) {

                    return false;

                }

                

                GuildMember member = memberOptional.get();

                if (member.getRole() != GuildMember.Role.LEADER) {

                    log.warn("Only guild leader can upgrade guild");

                    return false;

                }

                

                Guild guild = getGuild(member.getGuildId());

                if (guild == null) {

                    return false;

                }

                

                if (guild.getLevel() >= 10) {

                    log.warn("Guild is already at max level");

                    return false;

                }

                

                int requiredExp = guild.getLevel() * 10000;

                if (guild.getExp() < requiredExp) {

                    log.warn("Guild does not have enough exp to upgrade");

                    return false;

                }

                

                guild.setLevel(guild.getLevel() + 1);

                guild.setExp(guild.getExp() - requiredExp);

                guild.setMaxMembers(guild.getMaxMembers() + 10);

                

                guildRepository.save(guild);

                guildCache.put(guild.getId(), guild);

                

                log.info("Guild {} upgraded to level {}", guild.getId(), guild.getLevel());

                return true;

                

            } catch (Exception e) {

                log.error("Error upgrading guild for player {}", playerId, e);

                return false;

            }

        }, executorService);

    }

    

    @Transactional

    public boolean kickMember(Long operatorId, Long targetPlayerId) {

        try {

            Optional<GuildMember> operatorOptional = guildMemberRepository.findByPlayerId(operatorId);

            if (operatorOptional.isEmpty()) {

                return false;

            }

            

            GuildMember operator = operatorOptional.get();

            if (operator.getRole() != GuildMember.Role.LEADER && operator.getRole() != GuildMember.Role.DEPUTY) {

                log.warn("Only leader or deputy can kick members");

                return false;

            }

            

            Optional<GuildMember> targetOptional = guildMemberRepository.findByPlayerId(targetPlayerId);

            if (targetOptional.isEmpty()) {

                return false;

            }

            

            GuildMember target = targetOptional.get();

            

            if (!target.getGuildId().equals(operator.getGuildId())) {

                log.warn("Target player is not in the same guild");

                return false;

            }

            

            if (target.getRole() == GuildMember.Role.LEADER) {

                log.warn("Cannot kick guild leader");

                return false;

            }

            

            if (target.getRole() == GuildMember.Role.DEPUTY && operator.getRole() != GuildMember.Role.LEADER) {

                log.warn("Deputy cannot kick another deputy");

                return false;

            }

            

            guildMemberRepository.delete(target);

            

            Guild guild = getGuild(target.getGuildId());

            if (guild != null) {

                guild.setMemberCount(guild.getMemberCount() - 1);

                guildRepository.save(guild);

                guildCache.put(guild.getId(), guild);

            }

            

            log.info("Player {} kicked member {} from guild", operatorId, targetPlayerId);

            return true;

            

        } catch (Exception e) {

            log.error("Error kicking member {} by operator {}", targetPlayerId, operatorId, e);

            return false;

        }

    }

    

    @Transactional

    public boolean promoteMember(Long operatorId, Long targetPlayerId) {

        try {

            Optional<GuildMember> operatorOptional = guildMemberRepository.findByPlayerId(operatorId);

            if (operatorOptional.isEmpty()) {

                return false;

            }

            

            GuildMember operator = operatorOptional.get();

            if (operator.getRole() != GuildMember.Role.LEADER) {

                log.warn("Only guild leader can promote members");

                return false;

            }

            

            Optional<GuildMember> targetOptional = guildMemberRepository.findByPlayerId(targetPlayerId);

            if (targetOptional.isEmpty()) {

                return false;

            }

            

            GuildMember target = targetOptional.get();

            

            if (!target.getGuildId().equals(operator.getGuildId())) {

                log.warn("Target player is not in the same guild");

                return false;

            }

            

            if (target.getRole() != GuildMember.Role.MEMBER) {

                log.warn("Can only promote members to deputy");

                return false;

            }

            

            target.setRole(GuildMember.Role.DEPUTY);

            guildMemberRepository.save(target);

            

            log.info("Player {} promoted member {} to deputy", operatorId, targetPlayerId);

            return true;

            

        } catch (Exception e) {

            log.error("Error promoting member {} by operator {}", targetPlayerId, operatorId, e);

            return false;

        }

    }

    

    @Transactional

    public boolean demoteMember(Long operatorId, Long targetPlayerId) {

        try {

            Optional<GuildMember> operatorOptional = guildMemberRepository.findByPlayerId(operatorId);

            if (operatorOptional.isEmpty()) {

                return false;

            }

            

            GuildMember operator = operatorOptional.get();

            if (operator.getRole() != GuildMember.Role.LEADER) {

                log.warn("Only guild leader can demote members");

                return false;

            }

            

            Optional<GuildMember> targetOptional = guildMemberRepository.findByPlayerId(targetPlayerId);

            if (targetOptional.isEmpty()) {

                return false;

            }

            

            GuildMember target = targetOptional.get();

            

            if (!target.getGuildId().equals(operator.getGuildId())) {

                log.warn("Target player is not in the same guild");

                return false;

            }

            

            if (target.getRole() != GuildMember.Role.DEPUTY) {

                log.warn("Can only demote deputies to members");

                return false;

            }

            

            target.setRole(GuildMember.Role.MEMBER);

            guildMemberRepository.save(target);

            

            log.info("Player {} demoted member {} to regular member", operatorId, targetPlayerId);

            return true;

            

        } catch (Exception e) {

            log.error("Error demoting member {} by operator {}", targetPlayerId, operatorId, e);

            return false;

        }

    }

}

